package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.*;
import jodd.util.ArraysUtil;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.support.ValueType;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.swing.text.Highlighter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;


    @Override
    public SearchResponseVo doSearch(SearchParam searchParam) {

        try {
            SearchRequest searchRequest = buildDsl(searchParam);

            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            SearchResponseVo searchResponseVo = buidDslResponseVO(searchResponse);

            searchResponseVo.setPageNo(searchParam.getPageNo());
            searchResponseVo.setPageSize(searchParam.getPageSize());
            Long totalRecord = searchResponseVo.getTotal();
            Long totalPages = totalRecord / searchParam.getPageSize() + (totalRecord % searchParam.getPageSize() == 0 ? 0 : 1);
            searchResponseVo.setTotalPages(totalPages);

            return searchResponseVo;
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }


    }

    private SearchResponseVo buidDslResponseVO(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        SearchHits hits = searchResponse.getHits();
        SearchHit[] hitArry = hits.getHits();
        List<Goods> goodsList = new ArrayList<>();
        for (SearchHit searchHit : hitArry) {
            String sourceAsString = searchHit.getSourceAsString();
            Goods goods = JSONObject.parseObject(sourceAsString, Goods.class);
            goodsList.add(goods);

            Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            if (title != null) {
                Text[] fragments = title.fragments();
                Text text = fragments[0];
                goods.setTitle(text.toString());
            }

        }
        searchResponseVo.setGoodsList(goodsList);

        Aggregations aggregations = searchResponse.getAggregations();
        Map<String, Aggregation> asMap = aggregations.getAsMap();
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) asMap.get("tmIdAgg");
        List<? extends Terms.Bucket> buckets = tmIdAgg.getBuckets();
        List<SearchResponseTmVo> searchResponseTmVoList = new ArrayList<>();

        for (Terms.Bucket bucket : buckets) {
            String keyAsString = bucket.getKeyAsString();
            Long tmId = Long.parseLong(keyAsString);

            Map<String, Aggregation> asMap1 = bucket.getAggregations().getAsMap();
            ParsedStringTerms tmNameAgg = (ParsedStringTerms) asMap1.get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();

            ParsedStringTerms tmLogoUrlAgg = (ParsedStringTerms) asMap1.get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAgg.getBuckets().get(0).getKeyAsString();

            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();
            searchResponseTmVo.setTmId(tmId);
            searchResponseTmVo.setTmName(tmName);
            searchResponseTmVo.setTmLogoUrl(tmLogoUrl);
            searchResponseTmVoList.add(searchResponseTmVo);
        }
        searchResponseVo.setTrademarkList(searchResponseTmVoList);

        ParsedNested attrAgg = (ParsedNested) asMap.get("attrAgg");
        ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
        List<? extends Terms.Bucket> buckets1 = attrIdAgg.getBuckets();
        List<SearchResponseAttrVo> attrList = new ArrayList<>();
        for (Terms.Bucket bucket : buckets1) {
            String keyAsString = bucket.getKeyAsString();
            long keyAsLong = Long.parseLong(keyAsString);

            Map<String, Aggregation> asMap1 = bucket.getAggregations().getAsMap();

            ParsedStringTerms attrNameAgg = (ParsedStringTerms) asMap1.get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();

            ParsedStringTerms attrValueAgg = (ParsedStringTerms) asMap1.get("attrValueAgg");
            List<? extends Terms.Bucket> buckets2 = attrValueAgg.getBuckets();
            List<String> attrValueList = new ArrayList<>();
            for (Terms.Bucket bucket1 : buckets2) {
                String keyAsString1 = bucket1.getKeyAsString();
                attrValueList.add(keyAsString1);
            }
            SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
            searchResponseAttrVo.setAttrId(keyAsLong);
            searchResponseAttrVo.setAttrName(attrName);
            searchResponseAttrVo.setAttrValueList(attrValueList);
            attrList.add(searchResponseAttrVo);
        }
        searchResponseVo.setAttrsList(attrList);
        searchResponseVo.setTotal(hits.getTotalHits());
        return searchResponseVo;
    }


    private SearchRequest buildDsl(SearchParam searchParam) {

        //构建dsl整体
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //query bool
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();


        String keyword = searchParam.getKeyword();
        if (keyword != null && !"".equals(keyword)) {
            //query bool must
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyword).operator(Operator.AND));
        }

        Long category1Id = searchParam.getCategory1Id();
        Long category2Id = searchParam.getCategory2Id();
        Long category3Id = searchParam.getCategory3Id();
        if (category1Id != null) {
            //query bool filter term
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", category1Id));
        }
        if (category2Id != null) {
            //query bool filter term
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", category2Id));
        }
        if (category3Id != null) {
            //query bool filter term
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", category3Id));
        }

        //品牌id:品牌名称
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] split = trademark.split(":");
            String tmId = split[0];
            boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", tmId));
        }

        String[] props = searchParam.getProps();
        for (int i = 0; props!=null&&i < props.length; i++) {
            String prop = props[i];
            //平台属性id:平台属性值:平台属性名
            String[] split = prop.split(":");

            String attrId = split[0];
            String attrValue = split[1];
            String attrName = split[2];

            //query bool nested path --bool must
            BoolQueryBuilder attrBoolQuery = QueryBuilders.boolQuery();
            attrBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", attrId));
            attrBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", attrValue));

            NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrS",attrBoolQuery , ScoreMode.None);
            boolQueryBuilder.filter(nestedQueryBuilder);
        }
        searchSourceBuilder.query(boolQueryBuilder);

        // 1 热度值    2 价格    asc升序   desc降序
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = order.split(":");
            String orderType = split[0];
            String orderSort = split[1];

            String type = "1".equals(orderType) ? "hotScore" : "price";
            SortOrder sort = "ASC".equals(orderSort) ? SortOrder.ASC : SortOrder.DESC;

            searchSourceBuilder.sort(type, sort);
        }

        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();

        Integer pageNoNum = (pageNo - 1) * pageSize;

        searchSourceBuilder.from(pageNoNum);
        searchSourceBuilder.size(pageSize);

        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").preTags("<span style='color=red'>").postTags("</span>");
        searchSourceBuilder.highlighter(highlightBuilder);


        TermsAggregationBuilder termsAggregationBuilder = AggregationBuilders.terms("tmIdAgg")
                .field("tmId")
                .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"));
        searchSourceBuilder.aggregation(termsAggregationBuilder);

        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("attrAgg", "attrs")
                .subAggregation(
                        AggregationBuilders.terms("attrIdAgg")
                                .field("attrs.attrId")
                                .subAggregation(
                                        AggregationBuilders.terms("attrNameAgg")
                                                .field("attrs.attrName")
                                )
                                .subAggregation(
                                        AggregationBuilders.terms("attrValueAgg")
                                                .field("attrs.attrValue")
                                )
                );
        searchSourceBuilder.aggregation(nestedAggregationBuilder);

        String DSL = searchSourceBuilder.toString();
        System.out.println(DSL);
        SearchRequest searchRequest = new SearchRequest("goods");
        searchRequest.types("info");

        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }
}
